/*
*  Box3D.cpp
*
*  Created on: Jan 14, 2012
*  
*/

#pragma region INCLUDEs
//
#include "Box3D.h"
#include "Circle3D.h"
#include "MathHelper.h"

#include <iostream>
using namespace std;

#pragma endregion



namespace Physics
{
	//Re-Declare static variable for Compiler Pre-Processing
	int Box3D::bx3D_InstanceCount = 0;

#pragma region CONSTRUCTORS
	//C001: Default Constructor
	Box3D::Box3D()
	{
		this->length = 1;
		this->width = 1;
		this->height = 1;

		this->objID = ZERO;
		++bx3D_InstanceCount;
	}

	//C002: Overloaded Constructor
	Box3D::Box3D(const float length, const float width, const float height, const Vector3 &pos = Vector3() )
	{
		this->length = ( length < 1 ) ? 1 : length;
		this->width = ( width < 1 ) ? 1 : width;
		this->height = ( height < 1 ) ? 1 : height;

		this->position = pos;
		this->direction = Vector3();

		this->objID = ZERO;
		++bx3D_InstanceCount;
	}

	//C003: Overloaded Constructor
	Box3D::Box3D(const float length, const float width, const float height, const Vector3 &pos = Vector3(), const Vector3 &dir = Vector3() )
	{
		this->length = ( length < 1 ) ? 1 : length;
		this->width = ( width < 1 ) ? 1 : width;
		this->height = ( height < 1 ) ? 1 : height;

		this->position = pos;
		this->direction = dir;

		this->objID = ZERO;
		++bx3D_InstanceCount;
	}

	//C004: Overloaded Constructor
	Box3D::Box3D(const float length, const float width, const float height, const Vector3 &pos = Vector3(), const Vector3 &dir = Vector3(), const float speed = 0, const float mass = 0 )
	{
		this->length = ( length < 1 ) ? 1 : length;
		this->width = ( width < 1 ) ? 1 : width;
		this->height = ( height < 1 ) ? 1 : height;

		this->position = pos;
		this->direction = dir;

		this->speed = speed;
		this->mass = mass;

		this->objID = ZERO;
		++bx3D_InstanceCount;
	}

	//C005: COPY Constructor
	Box3D::Box3D( const Box3D &box )
	{
		this->length = box.getLength();
		this->width =  box.getWidth();
		this->height =  box.getHeight();

		this->position = box.getPosition();
		this->direction = box.getDirection();

		this->speed = box.getSpeed();
		this->mass = box.getMass();

		this->objID = box.getID();
		++bx3D_InstanceCount;
	}

	//C006: Assignment Operator
	Box3D& Box3D::operator=( const Box3D &box )
	{
		//Check for NO self assignment
		if (this != &box) 
		{
			this->length = box.getLength();
			this->width =  box.getWidth();
			this->height =  box.getHeight();

			this->position = box.getPosition();
			this->direction = box.getDirection();

			this->speed = box.getSpeed();
			this->mass = box.getMass();

			this->objID = box.getID();
			++bx3D_InstanceCount;
		}
			 return *this;
	}

	//C005: Virtual Destructor
	Box3D::~Box3D()
	{
		//decrement global active objects' counter
		IPhysicsObject::decrement_ObjectCount(); 

		//decrement box3D Instance Counter
		( ( bx3D_InstanceCount > 0 ) ? --bx3D_InstanceCount : bx3D_InstanceCount = 0 );
	}
#pragma endregion

	
	//M026: Getter Instance Count
	const int Box3D::count()
	{
		return bx3D_InstanceCount;
	}


#pragma region LENGTH_PROPERTIES
	//M001: Setter Length
	void Box3D::setLength(const float length)
	{
		this->length = ( length < 1 ) ? 1 : length;
	}

	//M002: Getter Length
	const float Box3D::getLength() const  { return this->length; }
	//
#pragma endregion


#pragma region WIDTH_PROPERTIES
	//M003: Setter Width
	void Box3D::setWidth(const float width)
	{
		this->width = ( width < 1 ) ? 1 : width;
	}

	//M004: Getter Width
	const float Box3D::getWidth() const  { return this->width; }
	//
#pragma endregion


#pragma region HEIGHT_PROPERTIES
	//M005: Setter Height
	void Box3D::setHeight(const float height)
	{
		this->height = ( height < 1 ) ? 1 : height;
	}

	//M006: Getter Height
	const float Box3D::getHeight() const  { return this->height; }
	//
#pragma endregion


#pragma region IPhysicsObject_3D_METHODs
	//M027: Print Summary Info about instance object
	void Box3D::Info()
	{
		cout << "\n======================================================\n" << endl;
		cout << "\nBox3D Obj Info:\n" << endl;
		cout << "\n--------------------------------------------------------\n" << endl;

		cout << "Object ID: " << this->objID << endl << endl;

		cout << "Position XYZ: (" << this->position.getX() << ", " << this->position.getY() << ", " << this->position.getZ() << ")" << endl;
		cout << "Direction XYZ: (" << this->direction.getX() << ", " << this->direction.getY() << ", " << this->direction.getZ() << ")" << endl;
		cout << "Speed: " << this->speed << endl;
		cout << "Mass: " << this->mass << endl;

		cout << "\nLength: " << this->length << endl;
		cout << "Width: " << this->width << endl;
		cout << "Height: " << this->height << endl;

		cout << "\n======================================================\n" << endl << endl;
	}
#pragma endregion


#pragma region ICOLLISION_3D_METHODS
	//M007: THIS -> Box3D collision check
	bool Box3D::collision(const Box3D &box)
	{
		Vector3 *box1 = new Vector3[8];
		Vector3 *box2 = new Vector3[8];

		bool hasCollided = false;

		box1[0] = this->position;
		box1[1] = Vector3( box1[0].getX(), box1[0].getY(), (box1[0].getZ() + this->length) );
		box1[2] = Vector3( (box1[0].getX() + this->width), box1[0].getY(), (box1[0].getZ() + this->length) );
		box1[3] = Vector3( (box1[0].getX() + this->width), box1[0].getY(), box1[0].getZ() );
		box1[4] = Vector3( (box1[0].getX() + this->width), (box1[0].getY() + this->height), box1[0].getZ() );
		box1[5] = Vector3( box1[0].getX(), (box1[0].getY() + this->height), box1[0].getZ() );
		box1[6] = Vector3( box1[0].getX(), (box1[0].getY() + this->height), (box1[0].getZ() + this->length) );
		box1[7] = Vector3( (box1[0].getX() + this->width), (box1[0].getY() + this->height), (box1[0].getZ() + this->length) );

		box2[0] = box.getPosition();
		box2[1] = Vector3( box2[0].getX(), box2[0].getY(), (box2[0].getZ() + box.getLength()) );
		box2[2] = Vector3( (box2[0].getX() + box.getWidth()), box2[0].getY(), (box2[0].getZ() + box.getLength()) );
		box2[3] = Vector3( (box2[0].getX() + box.getWidth()), box2[0].getY(), box2[0].getZ() );
		box2[4] = Vector3( (box2[0].getX() + box.getWidth()), (box2[0].getY() + box.getHeight()), box2[0].getZ() );
		box2[5] = Vector3( box2[0].getX(), (box2[0].getY() + box.getHeight()), box2[0].getZ() );
		box2[6] = Vector3( box2[0].getX(), (box2[0].getY() + box.getHeight()), (box2[0].getZ() + box.getLength()) );
		box2[7] = Vector3( (box2[0].getX() + box.getWidth()), (box2[0].getY() + box.getHeight()), (box2[0].getZ() + box.getLength()) );

		for(int i = 0; i < 8; i++)
		{
			bool inXbounds, inYbounds, inZbounds;

			inXbounds = inYbounds = inZbounds = false;

			// min - Top points of box
			// max - Bottom points of box
			inXbounds = ( ( (box1[i].getX() >= box2[0].getX()) && (box1[i].getX() >= box2[1].getX()) && (box1[i].getX() >= box2[5].getX()) && (box1[i].getX() >= box2[6].getX()) ) &&	// X-axis min
				( (box1[i].getX() <= box2[3].getX()) && (box1[i].getX() <= box2[2].getX()) && (box1[i].getX() <= box2[7].getX()) && (box1[i].getX() <= box2[4].getX()) ) );	// X-axis max

			inYbounds = ( ( (box1[i].getY() >= box2[0].getY()) && (box1[i].getY() >= box2[1].getY()) && (box1[i].getY() >= box2[2].getY()) && (box1[i].getY() >= box2[3].getY()) ) &&	// Y-axis min
				( (box1[i].getY() <= box2[5].getY()) && (box1[i].getY() <= box2[6].getY()) && (box1[i].getY() <= box2[4].getY()) && (box1[i].getY() <= box2[7].getY()) ) );	// Y-axis max;

			inZbounds = ( ( (box1[i].getZ() >= box2[0].getZ()) && (box1[i].getZ() >= box2[3].getZ()) && (box1[i].getZ() >= box2[4].getZ()) && (box1[i].getZ() >= box2[5].getZ()) ) &&	// Z-axis min
				( (box1[i].getZ() <= box2[1].getZ()) && (box1[i].getZ() <= box2[2].getZ()) && (box1[i].getZ() <= box2[7].getZ()) && (box1[i].getZ() <= box2[6].getZ()) ) );	// Z-axis max;

			if( inXbounds && inYbounds && inZbounds)
			{
				delete [] box1;
				delete [] box2;

				hasCollided = true;
			}
		}

		return hasCollided;
	}

	//M008: THIS -> Circle3D collision check
	bool Box3D::collision(const Circle3D &circle)
	{
		Vector3 center = circle.getCenter();
		
		float sqrDist = MathHelper::sqrDistancePointToCube(center, *this);
		float sqrRadius = circle.getRadius() * circle.getRadius();

		return ( (sqrDist > sqrRadius) ? false : true );
	}
#pragma endregion


#pragma region SHAPE_3D_METHODS
	//
	//M005: Getter Center
	const Vector3 Box3D::getCenter() const
	{
		float x = this->position.getX() + (this->width / 2);
		float y = this->position.getY() - (this->height / 2);
		float z = this->position.getZ() + (this->length / 2);

		return Vector3(x, y, z);
	}

	//==================== SETTER: ROTATION_3D ====================
	//Applied to XYZ Directions
	//-----------------------------
	//M006: Rotation input Vector3 in DEGREES	
	void Box3D::rotateInDegs(const Vector3 &degrees)
	{
		//====>> 2B cont-d from here <<===
		/*Vector3 vec = MathHelper::degreesToVector3(degrees);
		vec = (vec + this->direction);
		vec.normalize();
		this->direction = vec;
		*/
	}

	//M007: Rotation input float in DEGREES
	void Box3D::rotateInDegs(const float degrees)
	{
		//Implementation Code goes here...
	}

	//M008: Rotation input floats in DEGREES
	void Box3D::rotateInDegs(const float X, const float Y, const float Z)
	{
		//Implementation Code goes here...
	}		


	//Applied to XYZ Directions
	//-----------------------------
	//M009: Rotation input Vector3 in RADIANS
	void Box3D::rotateInRads(const Vector3 &radians)
	{
		//Implementation Code goes here...
	}

	//M010: Rotation input float in RADIANS
	void Box3D::rotateInRads(const float radians)
	{
		//Implementation Code goes here...
	}

	//M011: Rotation input floats in RADIANS
	void Box3D::rotateInRads(const float X, const float Y, const float Z)
	{
		//Implementation Code goes here...
	}


	//Applied to X-Direction
	//-----------------------------
	//M012: Rotation input float in DEGREES
	void Box3D::rotateX_InDegs(const float degrees)
	{
		//Implementation Code goes here...
	}

	//M013: Rotation input float in RADIANS
	void Box3D::rotateX_InRads(const float radians)
	{
		//Implementation Code goes here...
	}


	//Applied to Y-Direction
	//-----------------------------
	//M014: Rotation input float in DEGREES
	void Box3D::rotateY_InDegs(const float degrees)
	{
		//Implementation Code goes here...
	}

	//M015: Rotation input float in RADIANS
	void Box3D::rotateY_InRads(const float radians)
	{
		//Implementation Code goes here...
	}


	//Applied to Z-Direction
	//-----------------------------
	//M016: Rotation input float in DEGREES
	void Box3D::rotateZ_InDegs(const float degrees)
	{
		//Implementation Code goes here...
	}

	//M017: Rotation input float in RADIANS
	void Box3D::rotateZ_InRads(const float radians)
	{
		//Implementation Code goes here...
	}
	//==================== SETTER: ROTATION_3D ====================



	//==================== SETTER: TRANSLATION_3D ====================
	//TRANSLATION_POSITION
	//
	//M018: Translate Position using float
	void Box3D::translatePosition(const float X, const float Y, const float Z)
	{
		this->position.setXYZ( this->position.getX() + X, this->position.getY() + Y, this->position.getZ() + Z );
	}

	//M019: Translate Position using Vector3
	void Box3D::translatePosition(const Vector3 &value)
	{
		this->position.setXYZ( this->position + value );
	}

	//TRANSLATION_DIRECTION
	//
	//M020: Translate Direction using floats
	void Box3D::translateDirection(const float X, const float Y, const float Z)
	{
		//Implementation Code goes here...
	}

	//M021: Translate Direction using Vector3
	void Box3D::translateDirection(const Vector3 &value)							
	{
		//Implementation Code goes here...
	}
//==================== SETTER: TRANSLATION_3D ====================

#pragma endregion

} /* NAMESPACE */
